/**
 * @description: sea简单版
 * @date: 2021/1/2
 */

(function (global) {

    let seajs = {};
    let data = {};
    let cacheModules = {}; // 管理所有加载好的模块
    let defineData = null; // 临时存放define的数据

    const STATUS = {
        init: 0,
        fetching: 1,
        fetched: 2,
        loading: 3,
        loaded: 4,
        execed: 5
    }

    // 处理路径
    function dirname(path) {
        return path.match(/[^?#]*\//)[0];
    }
    const REG_SUPERIOR_PATH = /\/[^/]+?\/\.\./
    function resolve(id, refUri) {
        let dir = dirname(refUri || data.cwd) + id;

        dir = dir.replace(/(\/\.)+\//g, '/');

        while (REG_SUPERIOR_PATH.test(dir)) {
            dir = dir.replace(REG_SUPERIOR_PATH, '');
        }

        return dir;
    }

    let REQUIRE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g
    let SLASH_RE = /\\\\/g

    // 提取代码中的 require('xxx');
    function parseDependencies(code) {
        let ret = []

        code.replace(SLASH_RE, "")
            .replace(REQUIRE_RE, function(m, m1, m2) {
                if (m2) {
                    ret.push(m2)
                }
            })

        return ret
    }

    let nameIndex = 0;
    function getId() {
        return `_@module_${++nameIndex}`;
    }

    function loadScript(mod) {
        mod.status = STATUS.fetching;
        addOutsideResource('js', mod.id).then(() => {
            if (defineData) {
                mod.depends = defineData.deps;
                mod.factory = defineData.factory;
                mod.status = STATUS.fetched;
                defineData = null;
            }

            mod.load();
        })
    }

    function Module (id, deps) {
        this.id = id;
        this.depends = deps || [];
        this.exports = {};
        this.status = STATUS.init;
        this.uris = null;
        this._entry = []; // 传递入口函数用
    }

    function getModule (uri, deps) {
        return cacheModules[uri] || (cacheModules[uri] = new Module(uri, deps))
    }

    function commonRequire (id) {
        let mod = getModule(id);

        return mod && mod.exec();
    }

    Module.prototype.exec = function () {
        if (this.status === STATUS.execed) {
            return this.exports;
        }
        this.status = STATUS.execed;
        delete this._entry;

        let require = id => commonRequire(this.uris[id]);
        let exports = typeof this.factory === 'function' ?
            this.factory.call(global, require, this.exports, this) :
            this.factory;

        if (exports === undefined) {
            exports = this.exports
        }

        this.exports = exports;
        return this.exports;
    }

    // 判断依赖是否加载完，是则开始入口_entry的代码
    Module.prototype.onload = function () {
        this.status = STATUS.loaded;

        this._entry.forEach(item => {
            if (--item.remain === 0) {
                item.callback();
            }
        })

        delete this._entry;
    }

    // 将入口的callback传递到依赖中(自己不保存)，等依赖加载完执行，如果还有依赖就继续传递
    Module.prototype.pass = function () {
        for (let i = 0; i < this._entry.length; i++) {
            let entryMod = this._entry[i];
            let count = 0;
            this.depends.forEach((id) => {
                let depId = this.uris[id];
                let depMod = getModule(depId);

                // 循环依赖的话，就跳过，这样不会发生传递，回到load中执行onload
                if (depMod.status < STATUS.loaded && !entryMod.history.hasOwnProperty(depId)) {
                    entryMod.history[depId] = true;
                    count++;
                    depMod._entry .push(entryMod);
                }
            })

            if (count > 0) {
                entryMod.remain += count - 1; // 累加剩余依赖未加载数,-1是去掉this(已经传递则表面加载了)
                this._entry.shift();
                i--;
            }
        }
    }

    // 获取所有子依赖的完整路径
    Module.prototype.resolveDeps = function (refUri) {
        if (this.uris) {
            return this.uris;
        }

        this.uris = {};
        let deps = this.depends;
        deps.forEach(item => this.uris[item] = resolve(item, refUri));

        return this.uris;
    }

    // 处理并加载依赖
    Module.prototype.load = function () {
        if (this.status >= STATUS.loading) {
            return;
        }
        this.status = STATUS.loading;

        // 记录依赖的完整路径
        this.resolveDeps(this.id);
        this.pass();

        // 还存在说明是循环依赖，直接条用onload
        if (this._entry.length) {
            this.onload();
            return;
        }

        this.depends.forEach(id => {
            let depMod = getModule(this.uris[id]);
            if (depMod.status < STATUS.fetching) { // 还没下载过
                loadScript(depMod);
            } else if (depMod.status === STATUS.fetched) { // 下载完了
                depMod.load();
            }
        })
    }

    seajs.define = function (deps, factory) {
        if (typeof deps === 'function') {
            factory = deps;
            deps = parseDependencies(factory.toString());
        }

        defineData = {
            deps,
            factory
        }
    }

    seajs.use = function (deps, factory) {
        let mod = getModule(resolve(getId()), Array.isArray(deps) ? deps : [deps]);

        mod._entry.push(mod);
        mod.history = {};
        mod.remain = 1;

        mod.callback = function() {
            let exports = [];
            let uris = mod.resolveDeps();

            exports = mod.depends.map(depId => cacheModules[uris[depId]].exec())
            factory && factory.apply(global, exports);

            delete mod.callback;
            delete mod.history;
            delete mod.remain;
            delete mod._entry;
        }

        mod.load();
        return seajs;
    }

    function init() {
        data.cwd = dirname(location.href);
        !global.seajs && (global.seajs = seajs);
        !global.define && (global.define = seajs.define);
        seajs.define.cmd = {}
    }

    init();
})(this)
